#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <jni.h>
#include <sys/types.h>

#include "com_fricano_java42_instructional_example_jni_J42JNI_01.h"

int FUNCTION_01 = com_fricano_java42_instructional_example_jni_J42JNI_01_FUNCTION_01;
int FUNCTION_02 = com_fricano_java42_instructional_example_jni_J42JNI_01_FUNCTION_02;
int FUNCTION_04 = com_fricano_java42_instructional_example_jni_J42JNI_01_FUNCTION_04;
int FUNCTION_08 = com_fricano_java42_instructional_example_jni_J42JNI_01_FUNCTION_08;
int FUNCTION_10 = com_fricano_java42_instructional_example_jni_J42JNI_01_FUNCTION_10;
int FUNCTION_20 = com_fricano_java42_instructional_example_jni_J42JNI_01_FUNCTION_20;
int FUNCTION_40 = com_fricano_java42_instructional_example_jni_J42JNI_01_FUNCTION_40;
int FUNCTION_80 = com_fricano_java42_instructional_example_jni_J42JNI_01_FUNCTION_80;

static jmethodID ctor;
static jobject jObjectOut;
static jfieldID fidD;
static jfieldID fidI;
static jfieldID fidB;

static jdoubleArray arrayD;
static jdouble *pArrayD;
static jint arrayDsize;

static jintArray arrayI;
static jint *pArrayI;
static jint arrayIsize;

static jbyteArray arrayB;
static jbyte *pArrayB;
static jint arrayBsize;

static jint x;

static void initialize(JNIEnv * env, jclass class, int function);
static jobject execute(JNIEnv * env, int function);

JNIEXPORT jobject JNICALL Java_com_fricano_java42_instructional_example_jni_J42JNI_101_nativeService(JNIEnv *env, jobject objectIn, jint functionIn) {
    jobject object;
    jclass classIn = (*env)->GetObjectClass(env, objectIn);
    initialize(env, classIn, functionIn);
    object = execute(env, functionIn);
    flushall();
    return object;
}

JNIEXPORT jobject JNICALL Java_com_fricano_java42_instructional_example_jni_J42JNI_101_nativeServiceStatic(JNIEnv *env, jclass classIn, jint functionIn) {
    jobject object;
    initialize(env, classIn, functionIn);
    object = execute(env, functionIn);
    flushall();
    return object;
}

void initialize(JNIEnv * env, jclass class,  int function) {
    //---------------------------------------
    // Get FIDs of primitives
    //---------------------------------------
    jfieldID fidvarD = (*env)->GetFieldID(env, class, "varD", "D");
    jfieldID fidvarF= (*env)->GetFieldID(env, class, "varF", "F");
    jfieldID fidvarI= (*env)->GetFieldID(env, class, "varI", "I");
    jfieldID fidvarB= (*env)->GetFieldID(env, class, "varB", "B");
    //---------------------------------------

    jdouble javaD;
    jfloat javaF;
    jint javaI;
    jbyte javaB;

    printf("nativeSide: functionIn:%X\n", function);

    ctor = (*env)->GetMethodID(env, class, "<init>", "()V");
    jObjectOut = (*env)->NewObject(env, class, ctor, NULL );

    //---------------------------------------
    // Read primitive values
    //---------------------------------------
    javaD =(*env)->GetDoubleField(env, jObjectOut, fidvarD);
    javaF =(*env)->GetFloatField(env, jObjectOut, fidvarF);
    javaI =(*env)->GetIntField(env, jObjectOut, fidvarI);
    javaB =(*env)->GetByteField(env, jObjectOut, fidvarB);
    printf("nativeSide: double:%f float:%.4f int:%d byte:%d\n",javaD, javaF, javaI, javaB);
    //---------------------------------------

    //---------------------------------------
    // Get FIDs of primitive arrays
    //---------------------------------------
    fidD = (*env)->GetFieldID(env, class, "dArray", "[D");
    fidI = (*env)->GetFieldID(env, class, "iArray", "[I");
    fidB = (*env)->GetFieldID(env, class, "bArray", "[B");
    //---------------------------------------
}

jobject execute(JNIEnv * env, int function) {

    if ((function & FUNCTION_01) > 0) {
        arrayD = (jdoubleArray) (*env)->GetObjectField(env, jObjectOut, fidD);
        pArrayD = (*env)->GetDoubleArrayElements(env, arrayD, 0);
        arrayDsize = (*env)->GetArrayLength(env, arrayD);
        for (x = 0; x < arrayDsize; x++) {
            pArrayD[x] = x;
        }
        (*env)->ReleaseDoubleArrayElements(env, arrayD, pArrayD, 0);
    }

    if ((function & FUNCTION_02) > 0) {
        arrayI = (jintArray) (*env)->GetObjectField(env, jObjectOut, fidI);
        pArrayI = (*env)->GetIntArrayElements(env, arrayI, 0);
        arrayIsize = (*env)->GetArrayLength(env, arrayI);
        for (x = 0; x < arrayIsize; x++) {
            pArrayI[x] = x;
        }
        (*env)->ReleaseIntArrayElements(env, arrayI, pArrayI, 0);
    }

    if ((function & FUNCTION_04) > 0) {
        arrayB = (jbyteArray) (*env)->GetObjectField(env, jObjectOut, fidB);
        pArrayB = (*env)->GetByteArrayElements(env, arrayB, 0);
        arrayBsize = (*env)->GetArrayLength(env, arrayB);
        for (x = 0; x < arrayBsize; x++) {
            pArrayB[x] = x;
        }
        (*env)->ReleaseByteArrayElements(env, arrayB, pArrayB, 0);
    }

    return jObjectOut;
}

